#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <fcntl.h> // for _O_BINARY
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include "grate_constants.h"
#include "ext_rational.h"
#include "words_queue.h"
#include "dfa.h"
#include "dfa_construct.h"
#include "ad_construct.h"
#include "input_data.h"
#include "grate_config.h"

#include "grate_oracle.h"
#include "grate_simple.h"
#include "mrate.h"

using namespace std;

// Contains all the parameters
grate_config CONFIG;

// Main containers - queue and DFA table
words_queue * WORDS_QUEUE;
dfa * DFA;

//------------------------------------------- INITIALIZATION -----------------------------------

// Determines if queue size is more important than dfa size
int queue_more_important(input_data & INPUT){
  int alph_size = INPUT.ALPH_SIZE;
  if (alph_size <= 2) return 0;
  return (INPUT.beta < ext_rational(alph_size-1, alph_size-2, 1));
}

void init(input_data & INPUT){
  if (CONFIG.auto_queue_size == false){
    WORDS_QUEUE = create_words_queue(INPUT, CONFIG.QUEUE_SIZE);
  }
  if (CONFIG.auto_dfa_size == false){
    DFA = create_dfa(INPUT, CONFIG.DFA_SIZE);
  }
  
  if (queue_more_important(INPUT)){
    if (CONFIG.auto_queue_size) WORDS_QUEUE = create_words_queue(INPUT, -1);
    if (CONFIG.auto_dfa_size) DFA = create_dfa(INPUT, -1);
  }
  else{
    if (CONFIG.auto_dfa_size) DFA = create_dfa(INPUT, -1);
    if (CONFIG.auto_queue_size) WORDS_QUEUE = create_words_queue(INPUT, -1);
  }

  if (CONFIG.OUTPUT_PROGRESS){
    if (CONFIG.auto_queue_size) INPUT.dump("Amount of memory allocated for queue = %d bytes\n", sizeof(char)*WORDS_QUEUE->size);
    if (CONFIG.auto_dfa_size) INPUT.dump("Amount of memory allocated for DFA = %d bytes\n", sizeof(int)*(DFA->size)*INPUT.ALPH_SIZE);
  }
}

void solve(input_data & INPUT){

  INPUT.create_logfile();
  if (CONFIG.OUTPUT_PROGRESS){
    INPUT.dump_input();
  }

  init(INPUT);

  i64 max_words_queue_size = construct_antidictionary(CONFIG, INPUT, WORDS_QUEUE, DFA);

  if (CONFIG.OUTPUT_PROGRESS){
    INPUT.dump("Maximal queue size = %I64d\n", max_words_queue_size);
    INPUT.dump("The number of words = %d\n", DFA->words_num);
    INPUT.dump("The number of vertices = %d\n", DFA->total);
  }

  /*if (GUI_VERSION){
    if (use_long_mask) max_words_queue_size >>= 1;
    else max_words_queue_size >>= 2;
    printf("%I64d\n", max_words_queue_size);
    printf("%d\n", words_num);
    printf("%d\n", total);
  }*/

  construct_dfa(DFA);

  if (CONFIG.OUTPUT_PROGRESS){
    INPUT.dump("\nDFA built!\n\n");
  }

  double grate;
  if (CONFIG.USE_ORACLE){
    grate = calc_grate_oracle(DFA, INPUT, CONFIG);
  }
  else{
    grate = calc_grate_simple(DFA, INPUT, CONFIG);
  }

  INPUT.dump("Upper bound: %.13lf\n", grate);
  
  double mrate = calc_mrate(INPUT, CONFIG, grate);
  INPUT.dump("Lower bound: ");
  if (mrate == -1){
    INPUT.dump("exponent too small\n");
  }
  else if (mrate == -2){
    INPUT.dump("cannot be calculated\n");
  }
  else{
    INPUT.dump("%.13lf\n", mrate);
    INPUT.dump("Difference: %.13lf\n", grate - mrate);
  }
  INPUT.dump("\n");
  
  if (mrate < 0) printf("%d\n", (int)mrate);
  else printf("%.13lf\n", mrate);
  printf("%.13lf\n", grate);
  fflush(stdout);
  fclose(INPUT.logfile);
}

int main(int argc, char * argv[]){
  CONFIG.parse_config_file();
  input_data INPUT;
  // File IO
  if (argc >= 2){
    freopen(argv[1], "r", stdin);
    if (argc >= 3){
      freopen(argv[2], "w", stdout);
    }
    while (scanf("%d%d", &INPUT.ALPH_SIZE, &INPUT.ROOT_LENGTH) > 0){
      if (INPUT.ALPH_SIZE < 0) break;
      scanf("%d%d%d", &INPUT.beta.num, &INPUT.beta.denum, &INPUT.beta.p);
      scanf("%lf", &INPUT.delta);
      solve(INPUT);
    }
  }
  // Or console mode
  else{
    printf("Hello! Working in console mode now...\n");
    while (1){
      printf("Alphabet size: ");
      scanf("%d", &INPUT.ALPH_SIZE);
      printf("Max length of the root: ");
      scanf("%d", &INPUT.ROOT_LENGTH);
      printf("Beta numerator: ");
      scanf("%d", &INPUT.beta.num);
      printf("Beta denominator: ");
      scanf("%d", &INPUT.beta.denum);
      printf("Is there a + ?: ");
      scanf("%d", &INPUT.beta.p);
      printf("Delta (exponential form is acceptable): ");
      scanf("%lf", &INPUT.delta);
      solve(INPUT);
      printf("\nContinue? (0/1): ");
      int ch;
      scanf("%d", &ch);
      if (!ch) break;
    }
  }
  return 0;
}
